home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkGrab.c < prev    next >
C/C++ Source or Header  |  1995-06-23  |  47KB  |  1,514 lines

  1. /* 
  2.  * tkGrab.c --
  3.  *
  4.  *    This file provides procedures that implement grabs for Tk.
  5.  *
  6.  * Copyright (c) 1992-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  */
  12.  
  13. static char sccsid[] = "@(#) tkGrab.c 1.46 95/06/23 08:48:10";
  14.  
  15. #include "tkPort.h"
  16. #include "tkInt.h"
  17.  
  18. /*
  19.  * This module is ridiculously complicated... sorry about that.  The
  20.  * problem is that the grabs provided by the X server aren't very
  21.  * useful, so this module tries to create a more useful form of grab
  22.  * using the X server's rudimentary facilities.  This requires us to
  23.  * suppress some of the events coming from the X server and generate
  24.  * many additional events.  Managing the events is hard because of the
  25.  * asynchronous way that the event queue is processed.  The current
  26.  * solution uses an extra "grab event queue" for each display, where
  27.  * we put the event that we synthesize here.  Events in this queue
  28.  * are processed before any events in the normal X queue.  The grab
  29.  * event queue is needed (rather than just pushing events back on the
  30.  * X queue) so that we can keep a clear boundary between the events
  31.  * we've synthesized and those coming from the server, so that we can
  32.  * add new events after all the other synthesized events but before
  33.  * those from the server.
  34.  */
  35.  
  36. /*
  37.  * One of the following structures is kept for each event in the
  38.  * grab queue maintained by this module.
  39.  */
  40.  
  41. struct TkGrabEvent {
  42.     XEvent event;            /* Event to process. */
  43.     struct TkGrabEvent *nextPtr;    /* Next event in list, or NULL for
  44.                      * end of list. */
  45. };
  46.  
  47. /*
  48.  * Bit definitions for grabFlags field of TkDisplay structures:
  49.  *
  50.  * GRAB_GLOBAL            1 means this is a global grab (we grabbed via
  51.  *                the server so all applications are locked out).
  52.  *                0 means this is a local grab that affects
  53.  *                only this application.
  54.  * GRAB_TRIGGER_QUEUED        1 means that there are events in the grab
  55.  *                queue for this display and a fake event has
  56.  *                been pushed to cause the events on the grab
  57.  *                queue to be processed before any events on
  58.  *                the regular X event queue.
  59.  * GRAB_TEMP_GLOBAL        1 means we've temporarily grabbed via the
  60.  *                server because a button is down and we want
  61.  *                to make sure that we get the button-up
  62.  *                event.  The grab will be released when the
  63.  *                last mouse button goes up.
  64.  */
  65.  
  66. #define GRAB_GLOBAL        1
  67. #define GRAB_TRIGGER_QUEUED    2
  68. #define GRAB_TEMP_GLOBAL    4
  69.  
  70. /*
  71.  * The following magic value is stored in the "send_event" field of
  72.  * EnterNotify and LeaveNotify events that are generated in this
  73.  * file.  This allows us to separate "real" events coming from the
  74.  * server from those that we generated.
  75.  */
  76.  
  77. #define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac)
  78.  
  79. /*
  80.  * The following magic value stored in the "send_event" field of
  81.  * an event identifies it as a special dummy event to trigger a
  82.  * change in the grabWinPtr field of a display.  The "window"
  83.  * field of the event actually contains a (TkWindow *) value to
  84.  * put in grabWinPtr.
  85.  */
  86.  
  87. #define GRAB_WINDOW_EVENT_MAGIC ((Bool) 0x347321ab)
  88.  
  89. /*
  90.  * Mask that selects any of the state bits corresponding to buttons,
  91.  * plus masks that select individual buttons' bits:
  92.  */
  93.  
  94. #define ALL_BUTTONS \
  95.     (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
  96. static unsigned int buttonStates[] = {
  97.     Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
  98. };
  99.  
  100. /*
  101.  * Forward declarations for procedures declared later in this file:
  102.  */
  103.  
  104. static void        ChangeEventWindow _ANSI_ARGS_((XEvent *eventPtr,
  105.                 TkWindow *winPtr));
  106. static void        EatGrabEvents _ANSI_ARGS_((TkDisplay *dispPtr,
  107.                 unsigned int serial));
  108. static TkWindow *    FindCommonAncestor _ANSI_ARGS_((TkWindow *winPtr1,
  109.                 TkWindow *winPtr2, int *countPtr1,
  110.                 int *countPtr2));
  111. static void        MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
  112.                 TkWindow *destPtr, int mode, int leaveEvents,
  113.                 int EnterEvents));
  114. static void        PushTriggerEvent _ANSI_ARGS_((TkDisplay *dispPtr));
  115. static void        QueueGrabWindowChange _ANSI_ARGS_((TkDisplay *dispPtr,
  116.                 TkWindow *grabWinPtr));
  117. static void        ReleaseButtonGrab _ANSI_ARGS_((TkDisplay *dispPtr));
  118.  
  119. /*
  120.  *----------------------------------------------------------------------
  121.  *
  122.  * Tk_GrabCmd --
  123.  *
  124.  *    This procedure is invoked to process the "grab" Tcl command.
  125.  *    See the user documentation for details on what it does.
  126.  *
  127.  * Results:
  128.  *    A standard Tcl result.
  129.  *
  130.  * Side effects:
  131.  *    See the user documentation.
  132.  *
  133.  *----------------------------------------------------------------------
  134.  */
  135.  
  136.     /* ARGSUSED */
  137. int
  138. Tk_GrabCmd(clientData, interp, argc, argv)
  139.     ClientData clientData;    /* Main window associated with
  140.                  * interpreter. */
  141.     Tcl_Interp *interp;        /* Current interpreter. */
  142.     int argc;            /* Number of arguments. */
  143.     char **argv;        /* Argument strings. */
  144. {
  145.     int globalGrab, c;
  146.     Tk_Window tkwin;
  147.     TkDisplay *dispPtr;
  148.     size_t length;
  149.  
  150.     if (argc < 2) {
  151.     badArgs:
  152.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  153.         argv[0], " ?-global? window\" or \"", argv[0],
  154.         " option ?arg arg ...?\"", (char *) NULL);
  155.     return TCL_ERROR;
  156.     }
  157.     c = argv[1][0];
  158.     length = strlen(argv[1]);
  159.     if (c == '.') {
  160.     if (argc != 2) {
  161.         goto badArgs;
  162.     }
  163.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  164.     if (tkwin == NULL) {
  165.         return TCL_ERROR;
  166.     }
  167.     return Tk_Grab(interp, tkwin, 0);
  168.     } else if ((c == '-') && (strncmp(argv[1], "-global", length) == 0)
  169.         && (length >= 2)) {
  170.     if (argc != 3) {
  171.         goto badArgs;
  172.     }
  173.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  174.     if (tkwin == NULL) {
  175.         return TCL_ERROR;
  176.     }
  177.     return Tk_Grab(interp, tkwin, 1);
  178.     } else if ((c == 'c') && (strncmp(argv[1], "current", length) == 0)) {
  179.     if (argc > 3) {
  180.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  181.             argv[0], " current ?window?\"", (char *) NULL);
  182.         return TCL_ERROR;
  183.     }
  184.     if (argc == 3) {
  185.         tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  186.         if (tkwin == NULL) {
  187.         return TCL_ERROR;
  188.         }
  189.         dispPtr = ((TkWindow *) tkwin)->dispPtr;
  190.         if (dispPtr->eventualGrabWinPtr != NULL) {
  191.         interp->result = dispPtr->eventualGrabWinPtr->pathName;
  192.         }
  193.     } else {
  194.         for (dispPtr = tkDisplayList; dispPtr != NULL;
  195.             dispPtr = dispPtr->nextPtr) {
  196.         if (dispPtr->eventualGrabWinPtr != NULL) {
  197.             Tcl_AppendElement(interp,
  198.                 dispPtr->eventualGrabWinPtr->pathName);
  199.         }
  200.         }
  201.     }
  202.     return TCL_OK;
  203.     } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) {
  204.     if (argc != 3) {
  205.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  206.             argv[0], " release window\"", (char *) NULL);
  207.         return TCL_ERROR;
  208.     }
  209.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  210.     if (tkwin == NULL) {
  211.         Tcl_ResetResult(interp);
  212.     } else {
  213.         Tk_Ungrab(tkwin);
  214.     }
  215.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)
  216.         && (length >= 2)) {
  217.     if ((argc != 3) && (argc != 4)) {
  218.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  219.             argv[0], " set ?-global? window\"", (char *) NULL);
  220.         return TCL_ERROR;
  221.     }
  222.     if (argc == 3) {
  223.         globalGrab = 0;
  224.         tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  225.     } else {
  226.         globalGrab = 1;
  227.         length = strlen(argv[2]);
  228.         if ((strncmp(argv[2], "-global", length) != 0) || (length < 2)) {
  229.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  230.             "\": must be \"", argv[0], " set ?-global? window\"",
  231.             (char *) NULL);
  232.         return TCL_ERROR;
  233.         }
  234.         tkwin = Tk_NameToWindow(interp, argv[3], (Tk_Window) clientData);
  235.     }
  236.     if (tkwin == NULL) {
  237.         return TCL_ERROR;
  238.     }
  239.     return Tk_Grab(interp, tkwin, globalGrab);
  240.     } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0)
  241.         && (length >= 2)) {
  242.     TkWindow *winPtr;
  243.  
  244.     if (argc != 3) {
  245.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  246.             argv[0], " status window\"", (char *) NULL);
  247.         return TCL_ERROR;
  248.     }
  249.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2],
  250.         (Tk_Window) clientData);
  251.     if (winPtr == NULL) {
  252.         return TCL_ERROR;
  253.     }
  254.     dispPtr = winPtr->dispPtr;
  255.     if (dispPtr->eventualGrabWinPtr != winPtr) {
  256.         interp->result = "none";
  257.     } else if (dispPtr->grabFlags & GRAB_GLOBAL) {
  258.         interp->result = "global";
  259.     } else {
  260.         interp->result = "local";
  261.     }
  262.     } else {
  263.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  264.         "\": must be current, release, set, or status",
  265.         (char *) NULL);
  266.     return TCL_ERROR;
  267.     }
  268.     return TCL_OK;
  269. }
  270.  
  271. /*
  272.  *----------------------------------------------------------------------
  273.  *
  274.  * Tk_Grab --
  275.  *
  276.  *    Grabs the pointer and keyboard, so that mouse-related events are
  277.  *    only reported relative to a given window and its descendants.
  278.  *
  279.  * Results:
  280.  *    A standard Tcl result is returned.  TCL_OK is the normal return
  281.  *    value;  if the grab could not be set then TCL_ERROR is returned
  282.  *    and interp->result will hold an error message.
  283.  *
  284.  * Side effects:
  285.  *    Once this call completes successfully, no window outside the
  286.  *    tree rooted at tkwin will receive pointer- or keyboard-related
  287.  *    events until the next call to Tk_Ungrab.  If a previous grab was
  288.  *    in effect within this application, then it is replaced with a new
  289.  *    one.
  290.  *
  291.  *----------------------------------------------------------------------
  292.  */
  293.  
  294. int
  295. Tk_Grab(interp, tkwin, grabGlobal)
  296.     Tcl_Interp *interp;            /* Used for error reporting. */
  297.     Tk_Window tkwin;            /* Window on whose behalf the pointer
  298.                      * is to be grabbed. */
  299.     int grabGlobal;            /* Non-zero means issue a grab to the
  300.                      * server so that no other application
  301.                      * gets mouse or keyboard events.
  302.                      * Zero means the grab only applies
  303.                      * within this application. */
  304. {
  305.     int grabResult;
  306.     TkWindow *winPtr = (TkWindow *) tkwin;
  307.     TkDisplay *dispPtr = winPtr->dispPtr;
  308.     TkWindow *winPtr2;
  309.     unsigned int serial;
  310.  
  311.     ReleaseButtonGrab(dispPtr);
  312.     if (dispPtr->eventualGrabWinPtr != NULL) {
  313.     if ((dispPtr->eventualGrabWinPtr == winPtr)
  314.         && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
  315.         return TCL_OK;
  316.     }
  317.     if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) {
  318.         alreadyGrabbed:
  319.         interp->result = "grab failed: another application has grab";
  320.         return TCL_ERROR;
  321.     }
  322.     Tk_Ungrab(tkwin);
  323.     }
  324.  
  325.     Tk_MakeWindowExist(tkwin);
  326.     if (!grabGlobal) {
  327.     Window dummy1, dummy2;
  328.     int dummy3, dummy4, dummy5, dummy6;
  329.     unsigned int state;
  330.  
  331.     /*
  332.      * Local grab.  However, if any mouse buttons are down, turn
  333.      * it into a global grab temporarily, until the last button
  334.      * goes up.  This does two things: (a) it makes sure that we
  335.      * see the button-up event;  and (b) it allows us to track mouse
  336.      * motion among all of the windows of this application.
  337.      */
  338.  
  339.     dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
  340.     XQueryPointer(dispPtr->display, winPtr->window, &dummy1,
  341.         &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state);
  342.     if ((state & ALL_BUTTONS) != 0) {
  343.         dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
  344.         goto setGlobalGrab;
  345.     }
  346.     } else {
  347.     dispPtr->grabFlags |= GRAB_GLOBAL;
  348.     setGlobalGrab:
  349.  
  350.     /*
  351.      * Tricky point:  must ungrab before grabbing.  This is needed
  352.      * in case there is a button auto-grab already in effect.  If
  353.      * there is, and the mouse has moved to a different window, X
  354.      * won't generate enter and leave events to move the mouse if
  355.      * we grab without ungrabbing.
  356.      */
  357.  
  358.     XUngrabPointer(dispPtr->display, CurrentTime);
  359.     serial = NextRequest(dispPtr->display);
  360.     grabResult = XGrabPointer(dispPtr->display, winPtr->window,
  361.         True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
  362.         |PointerMotionMask, GrabModeAsync, GrabModeAsync, None,
  363.         None, CurrentTime);
  364.     if (grabResult != 0) {
  365.         grabError:
  366.         if (grabResult == GrabNotViewable) {
  367.         interp->result = "grab failed: window not viewable";
  368.         } else if (grabResult == AlreadyGrabbed) {
  369.         goto alreadyGrabbed;
  370.         } else if (grabResult == GrabFrozen) {
  371.         interp->result = "grab failed: keyboard or pointer frozen";
  372.         } else if (grabResult == GrabInvalidTime) {
  373.         interp->result = "grab failed: invalid time";
  374.         } else {
  375.         char msg[100];
  376.     
  377.         sprintf(msg, "grab failed for unknown reason (code %d)",
  378.             grabResult);
  379.         Tcl_AppendResult(interp, msg, (char *) NULL);
  380.         }
  381.         return TCL_ERROR;
  382.     }
  383.     grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
  384.         False, GrabModeAsync, GrabModeAsync, CurrentTime);
  385.     if (grabResult != 0) {
  386.         XUngrabPointer(dispPtr->display, CurrentTime);
  387.         goto grabError;
  388.     }
  389.  
  390.     /*
  391.      * Eat up any grab-related events generated by the server for the
  392.      * grab.  There are several reasons for doing this:
  393.      *
  394.      * 1. We have to synthesize the events for local grabs anyway, since
  395.      *    the server doesn't participate in them.
  396.      * 2. The server doesn't always generate the right events for global
  397.      *    grabs (e.g. it generates events even if the current window is
  398.      *    in the grab tree, which we don't want).
  399.      * 3. We want all the grab-related events to be processed immediately
  400.      *    (before other events that are already queued); events coming
  401.      *    from the server will be in the wrong place, but events we
  402.      *    synthesize here will go to the front of the queue.
  403.      */
  404.  
  405.     EatGrabEvents(dispPtr, serial);
  406.     }
  407.  
  408.     /*
  409.      * Synthesize leave events to move the pointer from its current window
  410.      * up to the lowest ancestor that it has in common with the grab window.
  411.      * However, only do this if the pointer is outside the grab window's
  412.      * subtree but inside the grab window's application.
  413.      */
  414.  
  415.     if ((dispPtr->serverWinPtr != NULL)
  416.         && (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) {
  417.     for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) {
  418.         if (winPtr2 == winPtr) {
  419.         break;
  420.         }
  421.         if (winPtr2 == NULL) {
  422.         MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0);
  423.         break;
  424.         }
  425.     }
  426.     }
  427.     QueueGrabWindowChange(dispPtr, winPtr);
  428.     return TCL_OK;
  429. }
  430.  
  431. /*
  432.  *----------------------------------------------------------------------
  433.  *
  434.  * Tk_Ungrab --
  435.  *
  436.  *    Releases a grab on the mouse pointer and keyboard, if there
  437.  *    is one set on the specified window.
  438.  *
  439.  * Results:
  440.  *    None.
  441.  *
  442.  * Side effects:
  443.  *    Pointer and keyboard events will start being delivered to other
  444.  *    windows again.
  445.  *
  446.  *----------------------------------------------------------------------
  447.  */
  448.  
  449. void
  450. Tk_Ungrab(tkwin)
  451.     Tk_Window tkwin;            /* Window that identifies display
  452.                      * for grab to be released. */
  453. {
  454.     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
  455.     TkWindow *grabWinPtr, *winPtr;
  456.     unsigned int serial;
  457.  
  458.     grabWinPtr = (TkWindow *) tkwin;
  459.     dispPtr = grabWinPtr->dispPtr;
  460.     if (grabWinPtr != dispPtr->eventualGrabWinPtr) {
  461.     return;
  462.     }
  463.     ReleaseButtonGrab(dispPtr);
  464.     QueueGrabWindowChange(dispPtr, (TkWindow *) NULL);
  465.     if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) {
  466.     dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
  467.     serial = NextRequest(dispPtr->display);
  468.     XUngrabPointer(dispPtr->display, CurrentTime);
  469.     XUngrabKeyboard(dispPtr->display, CurrentTime);
  470.     EatGrabEvents(dispPtr, serial);
  471.     }
  472.  
  473.     /*
  474.      * Generate events to move the pointer back to the window where it
  475.      * really is.  Some notes:
  476.      * 1. As with grabs, only do this if the "real" window is not a
  477.      *    descendant of the grab window, since in this case the pointer
  478.      *    is already where it's supposed to be.
  479.      * 2. If the "real" window is in some other application then don't
  480.      *    generate any events at all, since everything's already been
  481.      *    reported correctly.
  482.      * 3. Only generate enter events.  Don't generate leave events,
  483.      *    because we never told the lower-level windows that they
  484.      *    had the pointer in the first place.
  485.      */
  486.  
  487.     for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) {
  488.     if (winPtr == grabWinPtr) {
  489.         break;
  490.     }
  491.     if (winPtr == NULL) {
  492.         if ((dispPtr->serverWinPtr == NULL) ||
  493.             (dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) {
  494.         MovePointer2(grabWinPtr, dispPtr->serverWinPtr,
  495.             NotifyUngrab, 0, 1);
  496.         }
  497.         break;
  498.     }
  499.     }
  500. }
  501.  
  502. /*
  503.  *----------------------------------------------------------------------
  504.  *
  505.  * ReleaseButtonGrab --
  506.  *
  507.  *    This procedure is called to release a simulated button grab, if
  508.  *    there is one in effect.  A button grab is present whenever
  509.  *    dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL
  510.  *    flag is set.
  511.  *
  512.  * Results:
  513.  *    None.
  514.  *
  515.  * Side effects:
  516.  *    DispPtr->buttonWinPtr is reset to NULL, and enter and leave
  517.  *    events are generated if necessary to move the pointer from
  518.  *    the button grab window to its current window.
  519.  *
  520.  *----------------------------------------------------------------------
  521.  */
  522.  
  523. static void
  524. ReleaseButtonGrab(dispPtr)
  525.     register TkDisplay *dispPtr;    /* Display whose button grab is to be
  526.                      * released. */
  527. {
  528.     unsigned int serial;
  529.  
  530.     if (dispPtr->buttonWinPtr != NULL) {
  531.     if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) {
  532.         MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr,
  533.             NotifyUngrab, 1, 1);
  534.     }
  535.     dispPtr->buttonWinPtr = NULL;
  536.     }
  537.     if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) {
  538.     dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL;
  539.     serial = NextRequest(dispPtr->display);
  540.     XUngrabPointer(dispPtr->display, CurrentTime);
  541.     XUngrabKeyboard(dispPtr->display, CurrentTime);
  542.     EatGrabEvents(dispPtr, serial);
  543.     }
  544. }
  545.  
  546. /*
  547.  *----------------------------------------------------------------------
  548.  *
  549.  * TkPointerEvent --
  550.  *
  551.  *    This procedure is called for each pointer-related event, before
  552.  *    the event has been processed.  It does various things to make
  553.  *    grabs work correctly.
  554.  *
  555.  * Results:
  556.  *    If the return value is 1 it means the event should be processed
  557.  *    (event handlers should be invoked).  If the return value is 0
  558.  *    it means the event should be ignored in order to make grabs
  559.  *    work correctly.  In some cases this procedure modifies the event.
  560.  *
  561.  * Side effects:
  562.  *    Grab state information may be updated.  New events may also be
  563.  *    pushed back onto the event queue to replace or augment the
  564.  *    one passed in here.
  565.  *
  566.  *----------------------------------------------------------------------
  567.  */
  568.  
  569. int
  570. TkPointerEvent(eventPtr, winPtr)
  571.     register XEvent *eventPtr;        /* Pointer to the event. */
  572.     TkWindow *winPtr;            /* Tk's information for window
  573.                      * where event was reported. */
  574. {
  575.     register TkWindow *winPtr2;
  576.     TkDisplay *dispPtr = winPtr->dispPtr;
  577.     unsigned int serial;
  578.     int outsideGrabTree = 0;
  579.     int ancestorOfGrab = 0;
  580.     int appGrabbed = 0;            /* Non-zero means event is being
  581.                      * reported to an application that is
  582.                      * affected by the grab. */
  583.  
  584.     /*
  585.      * Collect information about the grab (if any).
  586.      */
  587.  
  588.     switch (TkGrabState(winPtr)) {
  589.     case TK_GRAB_IN_TREE:
  590.         appGrabbed = 1;
  591.         break;
  592.     case TK_GRAB_ANCESTOR:
  593.         appGrabbed = 1;
  594.         outsideGrabTree = 1;
  595.         ancestorOfGrab = 1;
  596.         break;
  597.     case TK_GRAB_EXCLUDED:
  598.         appGrabbed = 1;
  599.         outsideGrabTree = 1;
  600.         break;
  601.     }
  602.  
  603.     if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
  604.     /*
  605.      * Keep track of what window the mouse is *really* over.
  606.      * Any events that we generate have a special send_event value,
  607.      * which is detected below and used to ignore the event for
  608.      * purposes of setting serverWinPtr.
  609.      */
  610.  
  611.     if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) {
  612.         if ((eventPtr->type == LeaveNotify) &&
  613.             (winPtr->flags & TK_TOP_LEVEL)) {
  614.         dispPtr->serverWinPtr = NULL;
  615.         } else {
  616.         dispPtr->serverWinPtr = winPtr;
  617.         }
  618.     }
  619.  
  620.     /*
  621.      * When a grab is active, X continues to report enter and leave
  622.      * events for windows outside the tree of the grab window:
  623.      * 1. Detect these events and ignore them except for
  624.      *    windows above the grab window.
  625.      * 2. Allow Enter and Leave events to pass through the
  626.      *    windows above the grab window, but never let them
  627.      *    end up with the pointer *in* one of those windows.
  628.      */
  629.  
  630.     if (dispPtr->grabWinPtr != NULL) {
  631.         if (outsideGrabTree && appGrabbed) {
  632.         if (!ancestorOfGrab) {
  633.             return 0;
  634.         }
  635.         switch (eventPtr->xcrossing.detail) {
  636.             case NotifyInferior:
  637.             return 0;
  638.             case NotifyAncestor:
  639.             eventPtr->xcrossing.detail = NotifyVirtual;
  640.             break;
  641.             case NotifyNonlinear:
  642.             eventPtr->xcrossing.detail = NotifyNonlinearVirtual;
  643.             break;
  644.         }
  645.         }
  646.  
  647.         /*
  648.          * Make buttons have the same grab-like behavior inside a grab
  649.          * as they do outside a grab:  do this by ignoring enter and
  650.          * leave events except for the window in which the button was
  651.          * pressed.
  652.          */
  653.  
  654.         if ((dispPtr->buttonWinPtr != NULL)
  655.             && (winPtr != dispPtr->buttonWinPtr)) {
  656.         return 0;
  657.         }
  658.     }
  659.     return 1;
  660.     }
  661.     if (!appGrabbed) {
  662.     return 1;
  663.     }
  664.  
  665.     if (eventPtr->type == MotionNotify) {
  666.     /*
  667.      * When grabs are active, X reports motion events relative to the
  668.      * window under the pointer.  Instead, it should report the events
  669.      * relative to the window the button went down in, if there is a
  670.      * button down.  Otherwise, if the pointer window is outside the
  671.      * subtree of the grab window, the events should be reported
  672.      * relative to the grab window.  Otherwise, the event should be
  673.      * reported to the pointer window.
  674.      */
  675.  
  676.     winPtr2 = winPtr;
  677.     if (dispPtr->buttonWinPtr != NULL) {
  678.         winPtr2 = dispPtr->buttonWinPtr;
  679.     } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
  680.         winPtr2 = dispPtr->grabWinPtr;
  681.     }
  682.     if (winPtr2 != winPtr) {
  683.         XEvent newEvent;
  684.  
  685.         newEvent = *eventPtr;
  686.         ChangeEventWindow(&newEvent, winPtr2);
  687.         XPutBackEvent(winPtr2->display, &newEvent);
  688.         return 0;
  689.     }
  690.     return 1;
  691.     }
  692.  
  693.     /*
  694.      * Process ButtonPress and ButtonRelease events:
  695.      * 1. Keep track of whether a button is down and what window it
  696.      *    went down in.
  697.      * 2. If the first button goes down outside the grab tree, pretend
  698.      *    it went down in the grab window.  Note: it's important to
  699.      *    redirect events to the grab window like this in order to make
  700.      *    things like menus work, where button presses outside the
  701.      *    grabbed menu need to be seen.  An application can always
  702.      *    ignore the events if they occur outside its window.
  703.      * 3. If a button press or release occurs outside the window where
  704.      *    the first button was pressed, retarget the event so it's reported
  705.      *    to the window where the first button was pressed.
  706.      * 4. If the last button is released in a window different than where
  707.      *    the first button was pressed, generate Enter/Leave events to
  708.      *    move the mouse from the button window to its current window.
  709.      * 5. If the grab is set at a time when a button is already down, or
  710.      *    if the window where the button was pressed was deleted, then
  711.      *    dispPtr->buttonWinPtr will stay NULL.  Just forget about the
  712.      *    auto-grab for the button press;  events will go to whatever
  713.      *    window contains the pointer.  If this window isn't in the grab
  714.      *    tree then redirect events to the grab window.
  715.      * 6. When a button is pressed during a local grab, the X server sets
  716.      *    a grab of its own, since it doesn't even know about our local
  717.      *    grab.  This causes enter and leave events no longer to be
  718.      *    generated in the same way as for global grabs.  To eliminate this
  719.      *    problem, set a temporary global grab when the first button goes
  720.      *    down and release it when the last button comes up.
  721.      */
  722.  
  723.     if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
  724.     winPtr2 = dispPtr->buttonWinPtr;
  725.     if (winPtr2 == NULL) {
  726.         if (outsideGrabTree) {
  727.         winPtr2 = dispPtr->grabWinPtr;            /* Note 5. */
  728.         } else {
  729.         winPtr2 = winPtr;                /* Note 5. */
  730.         }
  731.     }
  732.     if (eventPtr->type == ButtonPress) {
  733.         if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
  734.         if (outsideGrabTree) {
  735.             XEvent newEvent;
  736.  
  737.             newEvent = *eventPtr;
  738.             ChangeEventWindow(&newEvent, dispPtr->grabWinPtr);
  739.             XPutBackEvent(dispPtr->display, &newEvent);
  740.             return 0;                    /* Note 2. */
  741.         }
  742.         if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {    /* Note 6. */
  743.             serial = NextRequest(dispPtr->display);
  744.             if (XGrabPointer(dispPtr->display,
  745.                 dispPtr->grabWinPtr->window, True,
  746.                 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
  747.                 GrabModeAsync, GrabModeAsync, None, None,
  748.                 CurrentTime) == 0) {
  749.             EatGrabEvents(dispPtr, serial);
  750.             if (XGrabKeyboard(dispPtr->display, winPtr->window,
  751.                 False, GrabModeAsync, GrabModeAsync,
  752.                 CurrentTime) == 0) {
  753.                 dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
  754.             } else {
  755.                 XUngrabPointer(dispPtr->display, CurrentTime);
  756.             }
  757.             }
  758.         }
  759.         dispPtr->buttonWinPtr = winPtr;
  760.         return 1;
  761.         }
  762.     } else {
  763.         if ((eventPtr->xbutton.state & ALL_BUTTONS)
  764.             == buttonStates[eventPtr->xbutton.button - Button1]) {
  765.         ReleaseButtonGrab(dispPtr);            /* Note 4. */
  766.         }
  767.     }
  768.     if (winPtr2 != winPtr) {
  769.         XEvent newEvent;
  770.  
  771.         newEvent = *eventPtr;
  772.         ChangeEventWindow(&newEvent, winPtr2);
  773.         XPutBackEvent(dispPtr->display, &newEvent);
  774.         return 0;                        /* Note 3. */
  775.     }
  776.     }
  777.  
  778.     return 1;
  779. }
  780.  
  781. /*
  782.  *----------------------------------------------------------------------
  783.  *
  784.  * ChangeEventWindow --
  785.  *
  786.  *    Given an event and a new window to which the event should be
  787.  *    retargeted, modify fields of the event so that the event is
  788.  *    properly retargeted to the new window.
  789.  *
  790.  * Results:
  791.  *    The following fields of eventPtr are modified:  window,
  792.  *    subwindow, x, y, same_screen.
  793.  *
  794.  * Side effects:
  795.  *    None.
  796.  *
  797.  *----------------------------------------------------------------------
  798.  */
  799.  
  800. static void
  801. ChangeEventWindow(eventPtr, winPtr)
  802.     register XEvent *eventPtr;    /* Event to retarget.  Must have
  803.                  * type ButtonPress, ButtonRelease, KeyPress,
  804.                  * KeyRelease, MotionNotify, EnterNotify,
  805.                  * or LeaveNotify. */
  806.     TkWindow *winPtr;        /* New target window for event. */
  807. {
  808.     int x, y, sameScreen, bd;
  809.     register TkWindow *childPtr;
  810.  
  811.     eventPtr->xmotion.window = Tk_WindowId(winPtr);
  812.     if (eventPtr->xmotion.root ==
  813.         RootWindow(winPtr->display, winPtr->screenNum)) {
  814.     Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
  815.     eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
  816.     eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
  817.     eventPtr->xmotion.subwindow = None;
  818.     for (childPtr = winPtr->childList; childPtr != NULL;
  819.         childPtr = childPtr->nextPtr) {
  820.         if (childPtr->flags & TK_TOP_LEVEL) {
  821.         continue;
  822.         }
  823.         x = eventPtr->xmotion.x - childPtr->changes.x;
  824.         y = eventPtr->xmotion.y - childPtr->changes.y;
  825.         bd = childPtr->changes.border_width;
  826.         if ((x >= -bd) && (y >= -bd)
  827.             && (x < (childPtr->changes.width + bd))
  828.             && (y < (childPtr->changes.height + bd))) {
  829.         eventPtr->xmotion.subwindow = childPtr->window;
  830.         }
  831.     }
  832.     sameScreen = 1;
  833.     } else {
  834.     eventPtr->xmotion.x = 0;
  835.     eventPtr->xmotion.y = 0;
  836.     eventPtr->xmotion.subwindow = None;
  837.     sameScreen = 0;
  838.     }
  839.     if (eventPtr->type == MotionNotify) {
  840.     eventPtr->xmotion.same_screen = sameScreen;
  841.     } else {
  842.     eventPtr->xbutton.same_screen = sameScreen;
  843.     }
  844. }
  845.  
  846. /*
  847.  *----------------------------------------------------------------------
  848.  *
  849.  * TkInOutEvents --
  850.  *
  851.  *    This procedure synthesizes EnterNotify and LeaveNotify events
  852.  *    to correctly transfer the pointer from one window to another.
  853.  *    It can also be used to generate FocusIn and FocusOut events
  854.  *    to move the input focus.
  855.  *
  856.  * Results:
  857.  *    None.
  858.  *
  859.  * Side effects:
  860.  *    Synthesized events may be pushed back onto the event queue.
  861.  *    The event pointed to by eventPtr is modified.
  862.  *
  863.  *----------------------------------------------------------------------
  864.  */
  865.  
  866. void
  867. TkInOutEvents(eventPtr, sourcePtr, destPtr, leaveType, enterType)
  868.     XEvent *eventPtr;        /* A template X event.  Must have all fields
  869.                  * properly set except for type, window,
  870.                  * subwindow, x, y, detail, and same_screen
  871.                  * (Not all of these fields are valid for
  872.                  * FocusIn/FocusOut events;  x_root and y_root
  873.                  * must be valid for Enter/Leave events, even
  874.                  * though x and y needn't be valid). */
  875.     TkWindow *sourcePtr;    /* Window that used to have the pointer or
  876.                  * focus (NULL means it was not in a window
  877.                  * managed by this process). */
  878.     TkWindow *destPtr;        /* Window that is to end up with the pointer
  879.                  * or focus (NULL means it's not one managed
  880.                  * by this process). */
  881.     int leaveType;        /* Type of events to generate for windows
  882.                  * being left (LeaveNotify or FocusOut).  0
  883.                  * means don't generate leave events. */
  884.     int enterType;        /* Type of events to generate for windows
  885.                  * being left (EnterNotify or FocusIn).  0
  886.                  * means don't generate enter events. */
  887. {
  888.     register TkWindow *winPtr;
  889.     int upLevels, downLevels, i, j, focus;
  890.  
  891.     /*
  892.      * There are four possible cases to deal with:
  893.      *
  894.      * 1. SourcePtr and destPtr are the same.  There's nothing to do in
  895.      *    this case.
  896.      * 2. SourcePtr is an ancestor of destPtr in the same top-level
  897.      *    window.  Must generate events down the window tree from source
  898.      *    to dest.
  899.      * 3. DestPtr is an ancestor of sourcePtr in the same top-level
  900.      *    window.  Must generate events up the window tree from sourcePtr
  901.      *    to destPtr.
  902.      * 4. All other cases.  Must first generate events up the window tree
  903.      *    from sourcePtr to its top-level, then down from destPtr's
  904.      *    top-level to destPtr. This form is called "non-linear."
  905.      *
  906.      * The call to FindCommonAncestor separates these four cases and decides
  907.      * how many levels up and down events have to be generated for.
  908.      */
  909.  
  910.     if (sourcePtr == destPtr) {
  911.     return;
  912.     }
  913.     if ((leaveType == FocusOut) || (enterType == FocusIn)) {
  914.     focus = 1;
  915.     } else {
  916.     focus = 0;
  917.     }
  918.     FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels);
  919.  
  920.     /*
  921.      * Generate enter/leave events and add them to the grab event queue.
  922.      */
  923.  
  924.  
  925. #define QUEUE(w, t, d)                    \
  926.     if (w->window != None) {                \
  927.     eventPtr->type = t;                \
  928.     if (focus) {                    \
  929.         eventPtr->xfocus.window = w->window;    \
  930.         eventPtr->xfocus.detail = d;        \
  931.     } else {                    \
  932.         eventPtr->xcrossing.detail = d;        \
  933.         ChangeEventWindow(eventPtr, w);        \
  934.     }                        \
  935.     TkQueueEvent(w->dispPtr, eventPtr);        \
  936.     }
  937.  
  938.     if (downLevels == 0) {
  939.     
  940.     /*
  941.      * SourcePtr is an inferior of destPtr.
  942.      */
  943.  
  944.     if (leaveType != 0) {
  945.         QUEUE(sourcePtr, leaveType, NotifyAncestor);
  946.         for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
  947.             winPtr = winPtr->parentPtr, i--) {
  948.         QUEUE(winPtr, leaveType, NotifyVirtual);
  949.         }
  950.     }
  951.     if ((enterType != 0) && (destPtr != NULL)) {
  952.         QUEUE(destPtr, enterType, NotifyInferior);
  953.     }
  954.     } else if (upLevels == 0) {
  955.  
  956.     /*
  957.      * DestPtr is an inferior of sourcePtr.
  958.      */
  959.  
  960.     if ((leaveType != 0) && (sourcePtr != NULL)) {
  961.         QUEUE(sourcePtr, leaveType, NotifyInferior);
  962.     }
  963.     if (enterType != 0) {
  964.         for (i = downLevels-1; i > 0; i--) {
  965.         for (winPtr = destPtr->parentPtr, j = 1; j < i;
  966.             winPtr = winPtr->parentPtr, j++) {
  967.         }
  968.         QUEUE(winPtr, enterType, NotifyVirtual);
  969.         }
  970.         if (destPtr != NULL) {
  971.         QUEUE(destPtr, enterType, NotifyAncestor);
  972.         }
  973.     }
  974.     } else {
  975.  
  976.     /*
  977.      * Non-linear:  neither window is an inferior of the other.
  978.      */
  979.  
  980.     if (leaveType != 0) {
  981.         QUEUE(sourcePtr, leaveType, NotifyNonlinear);
  982.         for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
  983.             winPtr = winPtr->parentPtr, i--) {
  984.         QUEUE(winPtr, leaveType, NotifyNonlinearVirtual);
  985.         }
  986.     }
  987.     if (enterType != 0) {
  988.         for (i = downLevels-1; i > 0; i--) {
  989.         for (winPtr = destPtr->parentPtr, j = 1; j < i;
  990.             winPtr = winPtr->parentPtr, j++) {
  991.         }
  992.         QUEUE(winPtr, enterType, NotifyNonlinearVirtual);
  993.         }
  994.         if (destPtr != NULL) {
  995.         QUEUE(destPtr, enterType, NotifyNonlinear);
  996.         }
  997.     }
  998.     }
  999. }
  1000.  
  1001. /*
  1002.  *----------------------------------------------------------------------
  1003.  *
  1004.  * TkQueueEvent --
  1005.  *
  1006.  *    This procedure adds an enter or leave event to the end of the
  1007.  *    grab queue for dispPtr.
  1008.  *
  1009.  * Results:
  1010.  *    None.
  1011.  *
  1012.  * Side effects:
  1013.  *    A copy of *eventPtr is added to the grab queue for dispPtr.
  1014.  *    It will be processed after all other events on the grab queue
  1015.  *    but before any "real" X events (those not coming from the grab
  1016.  *    queue).
  1017.  *
  1018.  *----------------------------------------------------------------------
  1019.  */
  1020.  
  1021. void
  1022. TkQueueEvent(dispPtr, eventPtr)
  1023.     TkDisplay *dispPtr;        /* Display for which the event is to be
  1024.                  * queued. */
  1025.     XEvent *eventPtr;        /* Event to queue. */
  1026. {
  1027.     TkGrabEvent *grabEventPtr;
  1028.  
  1029.     grabEventPtr = (TkGrabEvent *) ckalloc(sizeof(TkGrabEvent));
  1030.     grabEventPtr->event = *eventPtr;
  1031.     grabEventPtr->nextPtr = NULL;
  1032.     if (dispPtr->firstGrabEventPtr == NULL) {
  1033.     dispPtr->firstGrabEventPtr = grabEventPtr;
  1034.     } else {
  1035.     dispPtr->lastGrabEventPtr->nextPtr = grabEventPtr;
  1036.     }
  1037.     dispPtr->lastGrabEventPtr = grabEventPtr;
  1038.     if (!(dispPtr->grabFlags & GRAB_TRIGGER_QUEUED)) {
  1039.     PushTriggerEvent(dispPtr);
  1040.     }
  1041. }
  1042.  
  1043. /*
  1044.  *----------------------------------------------------------------------
  1045.  *
  1046.  * PushTriggerEvent --
  1047.  *
  1048.  *    This procedure creates a special "trigger" event and pushes it
  1049.  *    onto the front of the event queue for winPtr's display.
  1050.  *    When Tk_HandleEvent sees this event it will call back to this
  1051.  *    module so that we can feed events from the grab queue onto the
  1052.  *    front of the event queue.
  1053.  *
  1054.  * Results:
  1055.  *    None.
  1056.  *
  1057.  * Side effects:
  1058.  *    Causes TkGrabTriggerProc to be called later on when the event
  1059.  *    is actually handled.
  1060.  *
  1061.  *----------------------------------------------------------------------
  1062.  */
  1063.  
  1064. static void
  1065. PushTriggerEvent(dispPtr)
  1066.     TkDisplay *dispPtr;            /* Display on whose display a trigger
  1067.                      * event is to be pushed back. */
  1068. {
  1069.     XEvent event;
  1070.  
  1071.     event.xany.type = -1;
  1072.     event.xany.serial = 0;
  1073.     event.xany.send_event = True;
  1074.     event.xany.display = (Display *) dispPtr;
  1075.     event.xany.window = None;
  1076.     XPutBackEvent(dispPtr->display, &event);
  1077.     dispPtr->grabFlags |= GRAB_TRIGGER_QUEUED;
  1078. }
  1079.  
  1080. /*
  1081.  *----------------------------------------------------------------------
  1082.  *
  1083.  * TkGrabTriggerProc --
  1084.  *
  1085.  *    This procedure is invoked when a trigger event is encountered
  1086.  *    by Tk_HandleEvent. 
  1087.  *
  1088.  * Results:
  1089.  *    None.
  1090.  *
  1091.  * Side effects:
  1092.  *    See code below.
  1093.  *
  1094.  *----------------------------------------------------------------------
  1095.  */
  1096.  
  1097. void
  1098. TkGrabTriggerProc(eventPtr)
  1099.     XEvent *eventPtr;        /* Pointer to the trigger event. */
  1100. {
  1101.     TkDisplay *dispPtr = (TkDisplay *) eventPtr->xany.display;
  1102.     TkGrabEvent *grabEventPtr;
  1103.  
  1104.     /*
  1105.      * Remove the first event from the grab queue, if there is one.
  1106.      * If there are additional events left on the queue, then push
  1107.      * back a new trigger event so that this procedure will get called
  1108.      * again to process them.
  1109.      */
  1110.  
  1111.     dispPtr->grabFlags &= ~GRAB_TRIGGER_QUEUED;
  1112.     grabEventPtr = dispPtr->firstGrabEventPtr;
  1113.     if (grabEventPtr == NULL) {
  1114.     return;
  1115.     }
  1116.     dispPtr->firstGrabEventPtr = grabEventPtr->nextPtr;
  1117.     if (dispPtr->firstGrabEventPtr == NULL) {
  1118.     dispPtr->lastGrabEventPtr = NULL;
  1119.     } else {
  1120.     PushTriggerEvent(dispPtr);
  1121.     }
  1122.  
  1123.     /*
  1124.      * If this is a special event to change grabWinPtr, do that;  otherwise
  1125.      * call Tk_HandleEvent recursively to process the grab event. In either
  1126.      * case, free up the event when done.
  1127.      */
  1128.  
  1129.     if (grabEventPtr->event.xany.send_event == GRAB_WINDOW_EVENT_MAGIC) {
  1130.     dispPtr->grabWinPtr = (TkWindow *) Tk_IdToWindow(
  1131.         grabEventPtr->event.xany.display,
  1132.         grabEventPtr->event.xany.window);
  1133.     } else {
  1134.     Tk_HandleEvent(&grabEventPtr->event);
  1135.     }
  1136.     ckfree((char *) grabEventPtr);
  1137. }
  1138.  
  1139. /*
  1140.  *----------------------------------------------------------------------
  1141.  *
  1142.  * MovePointer2 --
  1143.  *
  1144.  *    This procedure synthesizes  EnterNotify and LeaveNotify events
  1145.  *    to correctly transfer the pointer from one window to another.
  1146.  *    It is different from MovePointer in that no template X event
  1147.  *    needs to be supplied;  this procedure generates the template
  1148.  *    event and calls MovePointer.
  1149.  *
  1150.  * Results:
  1151.  *    None.
  1152.  *
  1153.  * Side effects:
  1154.  *    Synthesized events may be pushed back onto the event queue.
  1155.  *
  1156.  *----------------------------------------------------------------------
  1157.  */
  1158.  
  1159. static void
  1160. MovePointer2(sourcePtr, destPtr, mode, leaveEvents, enterEvents)
  1161.     TkWindow *sourcePtr;    /* Window currently containing pointer (NULL
  1162.                  * means it's not one managed by this
  1163.                  * process). */
  1164.     TkWindow *destPtr;        /* Window that is to end up containing the
  1165.                  * pointer (NULL means it's not one managed
  1166.                  * by this process). */
  1167.     int mode;            /* Mode for enter/leave events, such as
  1168.                  * NotifyNormal or NotifyUngrab. */
  1169.     int leaveEvents;        /* Non-zero means generate leave events for the
  1170.                  * windows being left.  Zero means don't
  1171.                  * generate leave events. */
  1172.     int enterEvents;        /* Non-zero means generate enter events for the
  1173.                  * windows being entered.  Zero means don't
  1174.                  * generate enter events. */
  1175. {
  1176.     XEvent event;
  1177.     Window dummy1, dummy2;
  1178.     int dummy3, dummy4;
  1179.     TkWindow *winPtr;
  1180.  
  1181.     winPtr = sourcePtr;
  1182.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1183.     winPtr = destPtr;
  1184.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1185.         return;
  1186.     }
  1187.     }
  1188.  
  1189.     event.xcrossing.serial = LastKnownRequestProcessed(winPtr->display);
  1190.     event.xcrossing.send_event = GENERATED_EVENT_MAGIC;
  1191.     event.xcrossing.display = winPtr->display;
  1192.     event.xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum);
  1193.     event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
  1194.     XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
  1195.         &event.xcrossing.x_root, &event.xcrossing.y_root,
  1196.         &dummy3, &dummy4, &event.xcrossing.state);
  1197.     event.xcrossing.mode = mode;
  1198.     event.xcrossing.focus = False;
  1199.     TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0,
  1200.         (enterEvents) ? EnterNotify : 0);
  1201. }
  1202.  
  1203. /*
  1204.  *----------------------------------------------------------------------
  1205.  *
  1206.  * TkGrabDeadWindow --
  1207.  *
  1208.  *    This procedure is invoked whenever a window is deleted, so that
  1209.  *    grab-related cleanup can be performed.
  1210.  *
  1211.  * Results:
  1212.  *    None.
  1213.  *
  1214.  * Side effects:
  1215.  *    Various cleanups happen, such as generating events to move the
  1216.  *    pointer back to its "natural" window as if an ungrab had been
  1217.  *    done.  See the code.
  1218.  *
  1219.  *----------------------------------------------------------------------
  1220.  */
  1221.  
  1222. void
  1223. TkGrabDeadWindow(winPtr)
  1224.     register TkWindow *winPtr;        /* Window that is in the process
  1225.                      * of being deleted. */
  1226. {
  1227.     TkDisplay *dispPtr = winPtr->dispPtr;
  1228.  
  1229.     if (dispPtr->eventualGrabWinPtr == winPtr) {
  1230.     /*
  1231.      * Grab window was deleted.  Release the grab.
  1232.      */
  1233.  
  1234.     Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
  1235.     } else if (dispPtr->buttonWinPtr == winPtr) {
  1236.     ReleaseButtonGrab(dispPtr);
  1237.     }
  1238.     if (dispPtr->serverWinPtr == winPtr) {
  1239.     if (winPtr->flags & TK_TOP_LEVEL) {
  1240.         dispPtr->serverWinPtr = NULL;
  1241.     } else {
  1242.         dispPtr->serverWinPtr = winPtr->parentPtr;
  1243.     }
  1244.     }
  1245.     if (dispPtr->grabWinPtr == winPtr) {
  1246.     dispPtr->grabWinPtr = NULL;
  1247.     }
  1248. }
  1249.  
  1250. /*
  1251.  *----------------------------------------------------------------------
  1252.  *
  1253.  * EatGrabEvents --
  1254.  *
  1255.  *    This procedure is called to eliminate any Enter, Leave,
  1256.  *    FocusIn, or FocusOut events in the event queue for a
  1257.  *    display that have mode NotifyGrab or NotifyUngrab and
  1258.  *    have a serial number no less than a given value.
  1259.  *
  1260.  * Results:
  1261.  *    None.
  1262.  *
  1263.  * Side effects:
  1264.  *    DispPtr's display gets sync-ed, and some of the events get
  1265.  *    removed from its queue.  Unaffected events are initially
  1266.  *    removed from the queue but they are eventually put back again
  1267.  *    in the right order.
  1268.  *
  1269.  *----------------------------------------------------------------------
  1270.  */
  1271.  
  1272. static void
  1273. EatGrabEvents(dispPtr, serial)
  1274.     TkDisplay *dispPtr;        /* Display from which to consume events. */
  1275.     unsigned int serial;    /* Only discard events that have a serial
  1276.                  * number at least this great. */
  1277. {
  1278.     int numEvents, i, diff, mode;
  1279.     XEvent *events, *eventPtr;
  1280.  
  1281.     XSync(dispPtr->display, False);
  1282.     numEvents = QLength(dispPtr->display);
  1283.     if (numEvents == 0) {
  1284.     return;
  1285.     }
  1286.     events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
  1287.     for (i = 0; i < numEvents; i++) {
  1288.     XNextEvent(dispPtr->display, &events[i]);
  1289.     }
  1290.     for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
  1291.     /*
  1292.      * The diff caculation is trickier than it may seem.  Don't forget
  1293.      * that serial numbers can wrap around, so can't compare the two
  1294.      * serial numbers directly.
  1295.      */
  1296.  
  1297.     diff = eventPtr->xany.serial - serial;
  1298.     if ((eventPtr->type == EnterNotify)
  1299.         || (eventPtr->type == LeaveNotify)) {
  1300.         mode = eventPtr->xcrossing.mode;
  1301.     } else if ((eventPtr->type == FocusIn)
  1302.         || (eventPtr->type == FocusOut)) {
  1303.         mode = eventPtr->xfocus.mode;
  1304.     } else {
  1305.         mode = NotifyNormal;
  1306.     }
  1307.     if ((mode == NotifyNormal) || (diff < 0)) {
  1308.         XPutBackEvent(dispPtr->display, eventPtr);
  1309.     }
  1310.     }
  1311.     ckfree((char *) events);
  1312. }
  1313.  
  1314. /*
  1315.  *----------------------------------------------------------------------
  1316.  *
  1317.  * QueueGrabWindowChange --
  1318.  *
  1319.  *    This procedure queues a special event in the grab event queue
  1320.  *    for dispPtr, which will cause the "grabWinPtr" field for the
  1321.  *    display to get modified when the event is processed.  This
  1322.  *    procedure is needed to make sure that the grab window changes
  1323.  *    at the proper time relative to grab-related enter and leave
  1324.  *    events that are also in the queue.  In particular, this approach
  1325.  *    works even when multiple grabs and ungrabs happen back-to-back.
  1326.  *
  1327.  * Results:
  1328.  *    None.
  1329.  *
  1330.  * Side effects:
  1331.  *    DispPtr->grabWinPtr will be modified later (by TkGrabTriggerProc)
  1332.  *    when the event is removed from the grab event queue.
  1333.  *
  1334.  *----------------------------------------------------------------------
  1335.  */
  1336.  
  1337. static void
  1338. QueueGrabWindowChange(dispPtr, grabWinPtr)
  1339.     TkDisplay *dispPtr;        /* Display on which to change the grab
  1340.                  * window. */
  1341.     TkWindow *grabWinPtr;    /* Window that is to become the new grab
  1342.                  * window (may be NULL). */
  1343. {
  1344.     XEvent event;
  1345.  
  1346.     event.xany.display = dispPtr->display;
  1347.     event.xany.send_event = GRAB_WINDOW_EVENT_MAGIC;
  1348.     event.xany.window = (grabWinPtr == NULL) ? None : grabWinPtr->window;
  1349.     TkQueueEvent(dispPtr, &event);
  1350.     dispPtr->eventualGrabWinPtr = grabWinPtr;
  1351. }
  1352.  
  1353. /*
  1354.  *----------------------------------------------------------------------
  1355.  *
  1356.  * FindCommonAncestor --
  1357.  *
  1358.  *    Given two windows, this procedure finds their least common
  1359.  *    ancestor and also computes how many levels up this ancestor
  1360.  *    is from each of the original windows.
  1361.  *
  1362.  * Results:
  1363.  *    If the windows are in different applications or top-level
  1364.  *    windows, then NULL is returned and *countPtr1 and *countPtr2
  1365.  *    are set to the depths of the two windows in their respective
  1366.  *    top-level windows (1 means the window is a top-level, 2 means
  1367.  *    its parent is a top-level, and so on).  Otherwise, the return
  1368.  *    value is a pointer to the common ancestor and the counts are
  1369.  *    set to the distance of winPtr1 and winPtr2 from this ancestor
  1370.  *    (1 means they're children, 2 means grand-children, etc.).
  1371.  *
  1372.  * Side effects:
  1373.  *    None.
  1374.  *
  1375.  *----------------------------------------------------------------------
  1376.  */
  1377.  
  1378. static TkWindow *
  1379. FindCommonAncestor(winPtr1, winPtr2, countPtr1, countPtr2)
  1380.     TkWindow *winPtr1;        /* First window.   May be NULL. */
  1381.     TkWindow *winPtr2;        /* Second window.  May be NULL. */
  1382.     int *countPtr1;        /* Store nesting level of winPtr1 within
  1383.                  * common ancestor here. */
  1384.     int *countPtr2;        /* Store nesting level of winPtr2 within
  1385.                  * common ancestor here. */
  1386. {
  1387.     register TkWindow *winPtr;
  1388.     TkWindow *ancestorPtr;
  1389.     int count1, count2, i;
  1390.  
  1391.     /*
  1392.      * Mark winPtr1 and all of its ancestors with a special flag bit.
  1393.      */
  1394.  
  1395.     if (winPtr1 != NULL) {
  1396.     for (winPtr = winPtr1; winPtr != NULL; winPtr = winPtr->parentPtr) {
  1397.         winPtr->flags |= TK_GRAB_FLAG;
  1398.         if (winPtr->flags & TK_TOP_LEVEL) {
  1399.         break;
  1400.         }
  1401.     }
  1402.     }
  1403.  
  1404.     /*
  1405.      * Search upwards from winPtr2 until an ancestor of winPtr1 is
  1406.      * found or a top-level window is reached.
  1407.      */
  1408.  
  1409.     winPtr = winPtr2;
  1410.     count2 = 0;
  1411.     ancestorPtr = NULL;
  1412.     if (winPtr2 != NULL) {
  1413.     for (; winPtr != NULL; count2++, winPtr = winPtr->parentPtr) {
  1414.         if (winPtr->flags & TK_GRAB_FLAG) {
  1415.         ancestorPtr = winPtr;
  1416.         break;
  1417.         }
  1418.         if (winPtr->flags & TK_TOP_LEVEL)  {
  1419.         count2++;
  1420.         break;
  1421.         }
  1422.     }
  1423.     }
  1424.  
  1425.     /*
  1426.      * Search upwards from winPtr1 again, clearing the flag bits and
  1427.      * remembering how many levels up we had to go.
  1428.      */
  1429.  
  1430.     if (winPtr1 == NULL) {
  1431.     count1 = 0;
  1432.     } else {
  1433.     count1 = -1;
  1434.     for (i = 0, winPtr = winPtr1; winPtr != NULL;
  1435.         i++, winPtr = winPtr->parentPtr) {
  1436.         winPtr->flags &= ~TK_GRAB_FLAG;
  1437.         if (winPtr == ancestorPtr) {
  1438.         count1 = i;
  1439.         }
  1440.         if (winPtr->flags & TK_TOP_LEVEL) {
  1441.         if (count1 == -1) {
  1442.             count1 = i+1;
  1443.         }
  1444.         break;
  1445.         }
  1446.     }
  1447.     }
  1448.  
  1449.     *countPtr1 = count1;
  1450.     *countPtr2 = count2;
  1451.     return ancestorPtr;
  1452. }
  1453.  
  1454. /*
  1455.  *----------------------------------------------------------------------
  1456.  *
  1457.  * TkGrabState --
  1458.  *
  1459.  *    Given a window, this procedure returns a value that indicates
  1460.  *    the grab state of the application relative to the window.
  1461.  *
  1462.  * Results:
  1463.  *    The return value is one of three things:
  1464.  *        TK_GRAB_NONE -    no grab is in effect.
  1465.  *        TK_GRAB_IN_TREE -   there is a grab in effect, and winPtr
  1466.  *                is in the grabbed subtree.
  1467.  *        TK_GRAB_ANCESTOR -  there is a grab in effect;  winPtr is
  1468.  *                an ancestor of the grabbed window, in
  1469.  *                the same toplevel.
  1470.  *        TK_GRAB_EXCLUDED -    there is a grab in effect; winPtr is
  1471.  *                outside the tree of the grab and is not
  1472.  *                an ancestor of the grabbed window in the
  1473.  *                same toplevel.
  1474.  *
  1475.  * Side effects:
  1476.  *    None.
  1477.  *
  1478.  *----------------------------------------------------------------------
  1479.  */
  1480.  
  1481. int
  1482. TkGrabState(winPtr)
  1483.     TkWindow *winPtr;        /* Window for which grab information is
  1484.                  * needed. */
  1485. {
  1486.     TkWindow *grabWinPtr;
  1487.     TkWindow *winPtr2;
  1488.  
  1489.     grabWinPtr = winPtr->dispPtr->grabWinPtr;
  1490.     if (grabWinPtr == NULL) {
  1491.     return TK_GRAB_NONE;
  1492.     }
  1493.     if ((winPtr->mainPtr != grabWinPtr->mainPtr)
  1494.         && !(winPtr->dispPtr->grabFlags & GRAB_GLOBAL)) {
  1495.     return TK_GRAB_NONE;
  1496.     }
  1497.     for (winPtr2 = winPtr; winPtr2 != grabWinPtr;
  1498.         winPtr2 = winPtr2->parentPtr) {
  1499.     if (winPtr2 == NULL) {
  1500.         for (winPtr2 = grabWinPtr; winPtr2 != NULL;
  1501.             winPtr2 = winPtr2->parentPtr) {
  1502.         if (winPtr2 == winPtr) {
  1503.             return TK_GRAB_ANCESTOR;
  1504.         }
  1505.         if (winPtr2->flags & TK_TOP_LEVEL) {
  1506.             break;
  1507.         }
  1508.         }
  1509.         return TK_GRAB_EXCLUDED;
  1510.     }
  1511.     }
  1512.     return TK_GRAB_IN_TREE;
  1513. }
  1514.